#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "../umlcore/PartialElement.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class ValueSpecification;

/// <summary>
/// MultiplicityElement
/// A multiplicity is a definition of an inclusive interval of non-negative integers
/// beginning with a lower bound and ending with a (possibly infinite) upper bound.
/// A MultiplicityElement embeds this information to specify the allowable
/// cardinalities for an instantiation of the Element.
/// </summary>
class UMLMODEL_EXPORT MultiplicityElement : public PartialElement {

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// compatibleWith
    /// The operation compatibleWith takes another multiplicity as input. It returns
    /// true if the other multiplicity is wider than, or the same as, self.
    /// </summary>
    virtual bool compatibleWith(shared_ptr<MultiplicityElement> other) const;

    /// <summary>
    /// includesMultiplicity
    /// The query includesMultiplicity() checks whether this multiplicity includes all
    /// the cardinalities allowed by the specified multiplicity.
    /// </summary>
    virtual bool includesMultiplicity(shared_ptr<MultiplicityElement> M) const;

    /// <summary>
    /// is
    /// The operation is determines if the upper and lower bound of the ranges are the
    /// ones given.
    /// </summary>
    virtual bool is(int lowerbound, int upperbound) const;

    /// <summary>
    /// isMultivalued
    /// The query isMultivalued() checks whether this multiplicity has an upper bound
    /// greater than one.
    /// </summary>
    virtual bool isMultivalued() const;

    /// <summary>
    /// lowerBound
    /// The query lowerBound() returns the lower bound of the multiplicity as an
    /// integer, which is the integerValue of lowerValue, if this is given, and 1
    /// otherwise.
    /// </summary>
    virtual int lowerBound() const;

    /// <summary>
    /// upperBound
    /// The query upperBound() returns the upper bound of the multiplicity for a bounded
    /// multiplicity as an unlimited natural, which is the unlimitedNaturalValue of
    /// upperValue, if given, and 1, otherwise.
    /// </summary>
    virtual int upperBound() const;

public: // Properties
    /// <summary>
    /// isOrdered getter
    /// For a multivalued multiplicity, this attribute specifies whether the values in
    /// an instantiation of this MultiplicityElement are sequentially ordered.
    /// </summary>
    bool isOrdered() const;

    /// <summary>
    /// isOrdered setter
    /// For a multivalued multiplicity, this attribute specifies whether the values in
    /// an instantiation of this MultiplicityElement are sequentially ordered.
    /// </summary>
    void setIsOrdered(bool value);

    /// <summary>
    /// isUnique getter
    /// For a multivalued multiplicity, this attributes specifies whether the values in
    /// an instantiation of this MultiplicityElement are unique.
    /// </summary>
    bool isUnique() const;

    /// <summary>
    /// isUnique setter
    /// For a multivalued multiplicity, this attributes specifies whether the values in
    /// an instantiation of this MultiplicityElement are unique.
    /// </summary>
    void setIsUnique(bool value);

    /// <summary>
    /// lower getter
    /// The lower bound of the multiplicity interval.
    /// </summary>
    int lower() const;

    /// <summary>
    /// lower setter
    /// The lower bound of the multiplicity interval.
    /// </summary>
    void setLower(int value);

    /// <summary>
    /// lowerValue getter
    /// The specification of the lower bound for this multiplicity.
    /// </summary>
    shared_ptr<ValueSpecification> lowerValue() const;

    /// <summary>
    /// lowerValue setter
    /// The specification of the lower bound for this multiplicity.
    /// </summary>
    void setLowerValue(shared_ptr<ValueSpecification> value);

    /// <summary>
    /// upper getter
    /// The upper bound of the multiplicity interval.
    /// </summary>
    int upper() const;

    /// <summary>
    /// upper setter
    /// The upper bound of the multiplicity interval.
    /// </summary>
    void setUpper(int value);

    /// <summary>
    /// upperValue getter
    /// The specification of the upper bound for this multiplicity.
    /// </summary>
    shared_ptr<ValueSpecification> upperValue() const;

    /// <summary>
    /// upperValue setter
    /// The specification of the upper bound for this multiplicity.
    /// </summary>
    void setUpperValue(shared_ptr<ValueSpecification> value);

private: // Fields
    /// <summary>
    /// isOrdered
    /// For a multivalued multiplicity, this attribute specifies whether the values in
    /// an instantiation of this MultiplicityElement are sequentially ordered.
    /// </summary>
    const static bool isOrdered_default_;
    bool isOrdered_= isOrdered_default_;

    /// <summary>
    /// isUnique
    /// For a multivalued multiplicity, this attributes specifies whether the values in
    /// an instantiation of this MultiplicityElement are unique.
    /// </summary>
    const static bool isUnique_default_;
    bool isUnique_= isUnique_default_;

    /// <summary>
    /// lower
    /// The lower bound of the multiplicity interval.
    /// </summary>
    const static int lower_default_;
    int lower_= lower_default_;

    /// <summary>
    /// lowerValue
    /// The specification of the lower bound for this multiplicity.
    /// </summary>
    shared_ptr<ValueSpecification> lowerValue_;

    /// <summary>
    /// upper
    /// The upper bound of the multiplicity interval.
    /// </summary>
    const static int upper_default_;
    int upper_= upper_default_;

    /// <summary>
    /// upperValue
    /// The specification of the upper bound for this multiplicity.
    /// </summary>
    shared_ptr<ValueSpecification> upperValue_;

};

} // namespace uml
